package net.qiqbframework.modelling.command.fetching;


import net.qiqbframework.common.utils.StringUtils;
import net.qiqbframework.config.ConfigurationException;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.annotation.ExecutableHandlerWrapper;
import net.qiqbframework.messaging.annotation.MessageExecutableHandler;
import net.qiqbframework.messaging.annotation.WrappedMessageExecutableHandler;
import net.qiqbframework.modelling.command.handling.BusinessMessage;

import javax.annotation.Nonnull;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.Optional;

public class FetchExecutableHandlerWrapper implements ExecutableHandlerWrapper {

    @Override
    public MessageExecutableHandler wrap(@Nonnull MessageExecutableHandler original) {
        final Optional<Object> commandName = original.attribute("FetchHandler.commandName");
        if (commandName.isPresent()) {
            return new FetchExecutableHandlerImpl(original);
        }
        return original;

    }

    private static class FetchExecutableHandlerImpl
            extends WrappedMessageExecutableHandler
            implements FetchExecutableHandler {

        private final String aggregateName;
        private final String commandName;


        private FetchExecutableHandlerImpl(MessageExecutableHandler delegate) {
            super(delegate);

            Executable executable = delegate.unwrap(Executable.class).orElseThrow(() -> new ConfigurationException(
                    "The @CommandHandler annotation must be put on an Executable (either directly or as Meta " +
                            "Annotation)"));
            final String nameAttribute = delegate.attributes().get("FetchHandler.commandName");
            aggregateName = StringUtils.isEmptyOrNull(nameAttribute) ? delegate.payloadType().getName() : nameAttribute;
            if (executable instanceof Method method) {
                commandName = method.getDeclaringClass().getName();
                return;
            }
            throw new ConfigurationException("static @CommandHandler methods must declare a return value " +
                    "which is equal to or a subclass of the declaring type");

        }

        @Override
        public boolean canHandle(@Nonnull Message<?> message) {
            //
            return super.canHandle(message) && aggregateName.equals(((BusinessMessage<?>) message).getAggregateName());
        }


        @Override
        public String commandName() {
            return commandName;
        }

    }
}
